package com.iagent.core.resolver;

import com.iagent.annotation.*;
import com.iagent.bean.IagentBean;
import com.iagent.bean.IagentBean.IagentBeanBuilder;
import com.iagent.bean.IagentBeanWrapper;
import com.iagent.bean.IagentParamBean;
import com.iagent.bean.IagentParamBean.IagentParamBeanBuilder;
import com.iagent.constant.HttpEnum;
import com.iagent.core.resolver.annotation.AnnotationResolver;
import com.iagent.factory.IagentConfiguration;
import com.iagent.logging.LogFactory;
import com.iagent.logging.Logger;
import com.iagent.request.HttpExecutor;
import com.iagent.request.SimpleHttpExecutor;
import com.iagent.util.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author liujieyu
 * @date 2022/5/25 20:35
 * @desciption
 */
public class NativeInterfaceAnnotationResolver extends AbstractInterfaceAnnotationResolver {

    private static final Logger logger = LogFactory.getLogger(NativeInterfaceAnnotationResolver.class);

    public NativeInterfaceAnnotationResolver() {
        super(null);
    }

    public NativeInterfaceAnnotationResolver(IagentConfiguration configuration) {
        super(configuration);
    }

    @Override
    public void handlerInterfaceMethod(IagentBean pBean, Method method) {
        // create method iagent bean
        IagentBean methodBean = createIagentBean(pBean, method);
        //拿到当前接口执行器
        HttpExecutor httpExecutor = createHttpExecutor(pBean, method, methodBean);
        // 处理参数
        Parameter[] parameters = method.getParameters();
        //Method 参数建造者
        IagentParamBeanBuilder builder = IagentParamBeanBuilder.create()
                .setParameterClass(ClassUtils.getMethodParameterClass(method));
        for (int i = 0; i < parameters.length; i++) {
            try {
                for (AnnotationResolver annotationResolver : getParameterResolvers()) {
                    if (annotationResolver.isResolver(parameters[i])) { // 如果当前参数解析器能解析，则进行解析
                        annotationResolver.handleAnnotation(parameters[i], builder, i);
                    }
                }
            } catch (Throwable throwable) {
                logger.error("Resolver Method [" + ClassUtils.getClassPathByMethod(method) + "]", throwable);
                throw new IllegalArgumentException("Resolver Method [" + ClassUtils.getClassPathByMethod(method) + "] Error :" + throwable.getMessage());
            }
        };
        IagentParamBean paramBean = builder.build();
        // create wrapper method object, register to container
        IagentBeanWrapper wrapper = new IagentBeanWrapper();
        wrapper.setExecutor(httpExecutor);
        wrapper.setParamBean(paramBean);
        wrapper.setBean(methodBean);
        //set return class type
        wrapper.setReturnClassType(method.getReturnType());
        if (logger.isDebugEnabled()) {
            logger.debug("The method is " + method + ", the wrapper object is " + wrapper);
        }
        registerBeanWrapper(method, wrapper);
    }

    /**
     * handle parent iagent bean
     * @param clazz
     * @return
     */
    @Override
    public IagentBean handlerClassIagentBean(Class<?> clazz) {
        // get base info
        IagentUrl annotation = clazz.getAnnotation(IagentUrl.class);
        if (annotation == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("the class " + clazz + " is not has IagentUrl annotation");
            }
            return null;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("create parent iagent bean by " + clazz);
        }
        // create Iagent Bean Object
        return IagentBeanBuilder
                .create(getDefaultRequestConfig())
                .url(annotation.value())
                .requestType(annotation.requestType())
                .contentType(annotation.contentType())
                .connectionTime(annotation.connectionTime())
                .readTime(annotation.readTime())
                .httpExecutor(annotation.httpExecutor())
                .build();
    }

    /**
     * create method IagentBean object
     * @param pBean
     * @param method
     * @return
     */
    private IagentBean createIagentBean(IagentBean pBean, Method method) {
        // create new Object
        String parentUrl = pBean.getUrl();
        // resolver method annotation
        IagentUrl iagentUrl = getIagentUrl(method);

        String methodUrl = iagentUrl.value();
        if (StringUtils.isEmpty(methodUrl)) {
            methodUrl = getUrlByAnnotation(method);
        }
        Assert.notNull(methodUrl, "iagent url value is null");
        // get result url
        String requestUrl = null;
        if (parentUrl.endsWith(ResourceUtils.URL_SEPARATOR) && methodUrl.startsWith(ResourceUtils.URL_SEPARATOR)) {
            requestUrl = parentUrl.substring(0, parentUrl.length() - 1) + methodUrl;
        } else if (!parentUrl.endsWith(ResourceUtils.URL_SEPARATOR) && !methodUrl.startsWith(ResourceUtils.URL_SEPARATOR)) {
            requestUrl = parentUrl + ResourceUtils.URL_SEPARATOR + methodUrl;
        } else {
            requestUrl = parentUrl + methodUrl;
        }
        HttpEnum[] requestType = iagentUrl.requestType();
        if (requestType.length == 0) {
            requestType = new HttpEnum[]{pBean.getRequestType()};
        }
        String contentType = getContentTypeByAnnotation(method);
        if (StringUtils.isEmpty(contentType)) {
            contentType = pBean.getContentType();
        }
        int connectionTime = getConnectionTimeByAnnotation(method);
        if (connectionTime == -1) {
            connectionTime = pBean.getConnectionTime();
        }
        int readTime = getReadTimeByAnnotation(method);
        if (readTime == -1) {
            readTime = pBean.getReadTime();
        }
        if (logger.isDebugEnabled()) {
            logger.debug("create method iagent bean object by " + method);
        }
        // create method iagent bean, extend parent iagent bean
        return IagentBeanBuilder.create(null)
                .url(requestUrl)
                .requestType(requestType)
                .contentType(contentType)
                .connectionTime(connectionTime)
                .readTime(readTime)
                .returnClass(method.getReturnType())
                .httpExecutor(getAnnotationHttpExecutor(method))
                // record method
                .method(method)
                .build();
    }

    /**
     * get http executor by method annotation
     *
     * @return get http executor
     */
    private Class<? extends HttpExecutor> getAnnotationHttpExecutor(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            if (annotation instanceof IagentUrl) {
                return ((IagentUrl) annotation).httpExecutor();
            }
            if (annotation instanceof GetUrl) {
                return ((GetUrl) annotation).httpExecutor();
            }
            if (annotation instanceof PostUrl) {
                return ((PostUrl) annotation).httpExecutor();
            }
            if (annotation instanceof DeleteUrl) {
                return ((DeleteUrl) annotation).httpExecutor();
            }
            if (annotation instanceof PutUrl) {
                return ((PutUrl) annotation).httpExecutor();
            }
        }
        return null;
    }

    /**
     * create http executor
     * @param pBean
     * @param method
     * @return
     */
    private HttpExecutor createHttpExecutor(IagentBean pBean, Method method, IagentBean mBean) {
        // Get the current interface actuator
        HttpExecutor httpExecutor;
        Class<? extends HttpExecutor> clazz = mBean.getClazzHttpExecutor();
        // Cast to default executor
        if (SimpleHttpExecutor.class.isAssignableFrom(clazz)) {
            clazz = HttpExecutor.DEFAULT_EXECUTOR;
        }
        // Get the actuator on the current interface Class, on the Method inheritance class
        Class<? extends HttpExecutor> clazzHttpExecutor = pBean.getClazzHttpExecutor();
        if (defaultExecutor(clazz)) {
            clazz = clazzHttpExecutor;
        }

        if (containHttpExecutor(clazz)) {
            httpExecutor = getHttpExecutor(clazz);
        } else {
            httpExecutor = ClassUtils.newInstance(clazz);
            ReflectUtils.setFieldValue(httpExecutor, "configuration", getConfiguration());
            //注册到执行器容器中
            registerHttpExecutor(clazz, httpExecutor);
        }
        mBean.getRequestConfig().setHttpExecutor(httpExecutor.getClass());
        return httpExecutor;
    }

    /**
     * 判断是否为默认值
     * @param clazz
     * @return
     */
    private boolean defaultExecutor(Class<? extends HttpExecutor> clazz) {
        return ClassUtils.isEqualsClass(HttpExecutor.DEFAULT_EXECUTOR, clazz);
    }

    /**
     * 获取到IagentUrl注解参数,获取第一个注解
     * @param method
     * @return
     */
    private IagentUrl getIagentUrl(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            if (annotation instanceof IagentUrl) {
                return (IagentUrl) annotation;
            } else if (annotation instanceof GetUrl || annotation instanceof PostUrl ||
                    annotation instanceof DeleteUrl || annotation instanceof PutUrl) {
                return annotation.annotationType().getAnnotation(IagentUrl.class);
            }
        }

        return null;
    }

    /**
     * 获取快速注解上的value值
     * @param method
     * @return
     */
    private String getUrlByAnnotation(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            if (annotation instanceof GetUrl) {
                return ((GetUrl) annotation).value();
            }
            if (annotation instanceof PostUrl) {
                return ((PostUrl) annotation).value();
            }
            if (annotation instanceof DeleteUrl) {
                return ((DeleteUrl) annotation).value();
            }
            if (annotation instanceof PutUrl) {
                return ((PutUrl) annotation).value();
            }
        }

        return null;
    }


    /**
     * 获取快速注解上的contentType值
     * @param method
     * @return
     */
    private String getContentTypeByAnnotation(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            // Fix [#I5WQ4L]
            if (annotation instanceof IagentUrl) {
                return ((IagentUrl) annotation).contentType();
            }
            if (annotation instanceof GetUrl) {
                return ((GetUrl) annotation).contentType();
            }
            if (annotation instanceof PostUrl) {
                return ((PostUrl) annotation).contentType();
            }
            if (annotation instanceof DeleteUrl) {
                return ((DeleteUrl) annotation).contentType();
            }
            if (annotation instanceof PutUrl) {
                return ((PutUrl) annotation).contentType();
            }
        }

        return null;
    }

    /**
     * 获取快速注解上的 ConnectionTime 值
     * @param method
     * @return
     */
    private int getConnectionTimeByAnnotation(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            // Fix [#I5WQ4L]
            if (annotation instanceof IagentUrl) {
                return ((IagentUrl) annotation).connectionTime();
            }
            if (annotation instanceof GetUrl) {
                return ((GetUrl) annotation).connectionTime();
            }
            if (annotation instanceof PostUrl) {
                return ((PostUrl) annotation).connectionTime();
            }
            if (annotation instanceof DeleteUrl) {
                return ((DeleteUrl) annotation).connectionTime();
            }
            if (annotation instanceof PutUrl) {
                return ((PutUrl) annotation).connectionTime();
            }
        }

        return -1;
    }


    /**
     * 获取快速注解上的 readTime 值
     * @param method
     * @return
     */
    private int getReadTimeByAnnotation(Method method) {
        for (Annotation annotation : ClassUtils.getAnnotations(method)) {
            // Fix [#I5WQ4L]
            if (annotation instanceof IagentUrl) {
                return ((IagentUrl) annotation).readTime();
            }
            if (annotation instanceof GetUrl) {
                return ((GetUrl) annotation).readTime();
            }
            if (annotation instanceof PostUrl) {
                return ((PostUrl) annotation).readTime();
            }
            if (annotation instanceof DeleteUrl) {
                return ((DeleteUrl) annotation).readTime();
            }
            if (annotation instanceof PutUrl) {
                return ((PutUrl) annotation).readTime();
            }
        }

        return -1;
    }
}
